/*
 *  SceneNode.cpp
 *  EidoTron
 *
 *  Created by Luca Di Franco on 25/03/11.
 *  Copyright 2011 Luca Di Franco. All rights reserved.
 *
 */

#include "SceneNode.h"

#include <SDL_opengl.h>

#include "CollisionManager.h"
#include "Collider.h"
#include "../Entities/Entity.h"
#include "../Entities/Camera.h"

SceneNode* SceneNode::rootSceneNode = NULL;

SceneNode* SceneNode::playerNode = NULL;

bool SceneNode::showCollisionSolids = false; 

#pragma mark Static functions

SceneNode* SceneNode::getRootSceneNode()
{
	if (rootSceneNode == NULL)
		rootSceneNode = new SceneNode(NULL);
	return rootSceneNode;
}

SceneNode* SceneNode::getPlayerNode()
{
	return playerNode;
}

void SceneNode::placeCamera()
{
	glLoadIdentity();
	SceneNode* currentNode = rootSceneNode;
	int startDepth;
	glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &startDepth);
	do
	{
		glPushMatrix();
		glMultMatrixf(currentNode->translationMatrix);
		glMultMatrixf(currentNode->rotationMatrix);
		Camera* camera = dynamic_cast<Camera*>(currentNode->data);
		if (camera != NULL)
		{
			camera->renderEntity();
			float currentMatrix[16];
			glGetFloatv(GL_MODELVIEW_MATRIX, currentMatrix);
			int endDepth;
			glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &endDepth);
			for (int i = 0; i < endDepth - startDepth; i++)
				glPopMatrix();
			glLoadMatrixf(currentMatrix);
			return;
		}
		currentNode = SceneNode::nextNode(currentNode);
	}
	while (currentNode != NULL);
}

void SceneNode::renderTree()
{
	glPushMatrix();
	SceneNode::placeCamera();
	SceneNode* currentNode = rootSceneNode;
	do 
	{
		glPushMatrix();
		glMultMatrixf(currentNode->translationMatrix);
		glMultMatrixf(currentNode->rotationMatrix);
		float currentMatrix[16];
		glGetFloatv(GL_MODELVIEW_MATRIX, currentMatrix);
		currentNode->lastKnownPosition.x = currentMatrix[12];
		currentNode->lastKnownPosition.y = currentMatrix[13];
		currentNode->lastKnownPosition.z = currentMatrix[14];
		Camera* cameraTest = dynamic_cast<Camera*>(currentNode->data);
		if ((currentNode->data != NULL) && (cameraTest == NULL))
			currentNode->data->renderEntity();
		currentNode = SceneNode::nextNode(currentNode);
	}
	while (currentNode != NULL);
	glPopMatrix();
	CollisionManager::checkCollisions();
	if (!showCollisionSolids)
		return;
	glPushMatrix();
	SceneNode::placeCamera();
	currentNode = rootSceneNode;
	do 
	{
		glPushMatrix();
		glMultMatrixf(currentNode->translationMatrix);
		glMultMatrixf(currentNode->rotationMatrix);
		Camera* cameraTest = dynamic_cast<Camera*>(currentNode->data);
		if ((currentNode->collider != NULL) && (cameraTest == NULL))
			currentNode->collider->render();
		currentNode = SceneNode::nextNode(currentNode);
	}
	while (currentNode != NULL);
	glPopMatrix();
}

SceneNode* SceneNode::nextNode(SceneNode* currentNode)
{
	if (currentNode->firtsChild != NULL)
		return currentNode->firtsChild;
	else if (currentNode->rightBrother != NULL)
	{
		glPopMatrix();
		return currentNode->rightBrother;
	}
	else 
	{
		while ((currentNode->parent != NULL) && (currentNode->rightBrother == NULL))
		{
			glPopMatrix();
			currentNode = currentNode->parent;
		}
		glPopMatrix();
		return currentNode->rightBrother;
	}
}

#pragma mark -
#pragma mark Instance functions

SceneNode::SceneNode(Entity* entity)
{
	data = entity;
	collider = NULL;
	parent = rightBrother = firtsChild = NULL;
	for (int i = 0; i < 16; i++)
		translationMatrix[i] = rotationMatrix[i] = (i % 5 == 0) ? 1.0f : 0.0f;
}

SceneNode::~SceneNode()
{
	delete data;
	CollisionManager::remove(collider);
	delete collider;
}

SceneNode* SceneNode::createChild(Entity* entity)
{
	SceneNode* child = new SceneNode(entity);
	child->parent = this;
	if (this->firtsChild == NULL)
	{
		this->firtsChild = child;
		return child;
	}
	SceneNode* lastChild = this->firtsChild;
	while (lastChild->rightBrother != NULL)
		lastChild = lastChild->rightBrother;
	lastChild->rightBrother = child;
	return child;
}

SceneNode* SceneNode::createChildPlayerNode(Entity* entity)
{
	SceneNode* child = createChild(entity);
	if (playerNode == NULL)
		playerNode = child;
	return child;
}

void SceneNode::setTranslate(float x, float y, float z)
{
	translationMatrix[12] = x;
	translationMatrix[13] = y;
	translationMatrix[14] = z;
}

void SceneNode::memTranslate(float x, float y, float z)
{
	translationMatrix[12] += x;
	translationMatrix[13] += y;
	translationMatrix[14] += z;
}

void SceneNode::memRotate(float angle, float x, float y, float z)
{
	glPushAttrib(GL_TRANSFORM_BIT);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadMatrixf(rotationMatrix);
	glRotatef(angle, x, y, z);
	glGetFloatv(GL_MODELVIEW_MATRIX, rotationMatrix);
	glPopMatrix();
	glPopAttrib();
}

void SceneNode::memMove(float distance)
{
	glPushAttrib(GL_TRANSFORM_BIT);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadMatrixf(rotationMatrix);
	glTranslatef(0.0f, 0.0f, distance);
	float translation[16]; 
	glGetFloatv(GL_MODELVIEW_MATRIX, translation);
	glPopMatrix();
	glPopAttrib();
	memTranslate(translation[12], translation[13], translation[14]);
}